#include <stdafx.h>
#include "MaterialSceneParameters.h"

namespace Lit
{
	namespace Editor
	{
		namespace Material
		{
			SceneParameters::Light::Light( const Ogre::Light::LightTypes & type_, const Vec3 & pos, const Vec3& look_at_pt, const Color& diff, const Color& spec )
				: type(type_), position(pos), direction( (look_at_pt-pos).normalisedCopy() ), diffuse(diff), specular(spec){}

			bool SceneParameters::Light::created() const{ return ptr; }
			void SceneParameters::Light::destroy( RefPtr<Ogre::SceneManager> mgr )
			{
				if( !ptr || !mgr ) return;
				Ogre::SceneNode * n = this->ptr->getParentSceneNode();
				this->ptr->detachFromParent();
				mgr->destroyLight( *this->ptr );
				mgr->destroySceneNode( n );
				ptr = NULL;
			}
			void SceneParameters::Light::create( RefPtr<Ogre::SceneManager> mgr, RefPtr<Ogre::SceneNode> node )
			{
				if( !mgr ) return;
				if( created() ) destroy(mgr);
				this->ptr = mgr->createLight();
				Ogre::SceneNode * n = (node?*node:mgr->getRootSceneNode())->createChildSceneNode();
				n->attachObject( *this->ptr );

				sync(mgr);
			}
			void SceneParameters::Light::sync(RefPtr<Ogre::SceneManager> mgr, RefPtr<Ogre::SceneNode> node )
			{
				if( !created() ) create(mgr,node);
				this->ptr->setType( type );
				this->ptr->setPosition( position );
				this->ptr->setDirection( direction );
				this->ptr->setDiffuseColour( diffuse );
				this->ptr->setSpecularColour( specular );
			}
						
			Ogre::Light * SceneParameters::Light::operator->(){ return *ptr; }
			const Ogre::Light * SceneParameters::Light::operator->() const{ return *ptr; }
			Ogre::Light * SceneParameters::Light::operator*(){ return *ptr; }
			const Ogre::Light * SceneParameters::Light::operator*() const{ return *ptr; }

			SceneParameters::Entity::Entity( Ogre::Entity * ent ) 
				: ptr(ent)
				, mesh(ent?ent->getMesh()->getName():"")
				, scale( ent?ent->getParentSceneNode()->getScale():Vec3::UNIT_SCALE )
			{}
			SceneParameters::Entity::Entity( const std::string &mesh_name, const Vec3& scale_ ) 
				: mesh(mesh_name),scale( scale_ )
			{}
						
			Ogre::Entity * SceneParameters::Entity::operator->(){ return *ptr; }
			const Ogre::Entity * SceneParameters::Entity::operator->() const{ return *ptr; }
			Ogre::Entity * SceneParameters::Entity::operator*(){ return *ptr; }
			const Ogre::Entity * SceneParameters::Entity::operator*() const{ return *ptr; }

			bool SceneParameters::Entity::created() const{ return ptr; }
			void SceneParameters::Entity::destroy( RefPtr<Ogre::SceneManager> mgr )
			{
				if( !created() || !mgr ) return;
				Ogre::SceneNode * n = this->ptr->getParentSceneNode();
				this->ptr->detachFromParent();
				mgr->destroyEntity( *(this->ptr) );
				mgr->destroySceneNode( n );

				this->ptr = NULL;
			}
			void SceneParameters::Entity::create( RefPtr<Ogre::SceneManager> mgr, RefPtr<Ogre::SceneNode> node )
			{
				if( !mgr ) return;
				if( created() ) destroy(mgr);
				try
				{
					this->ptr = mgr->createEntity(mesh);
				}
				catch( ... )
				{
					this->ptr = mgr->createEntity( Ogre::SceneManager::PT_SPHERE );
				}
				Ogre::SceneNode * n = (node?*node:mgr->getRootSceneNode())->createChildSceneNode();
				n->attachObject( *this->ptr );
				sync( mgr );
			}
			void SceneParameters::Entity::sync( RefPtr<Ogre::SceneManager> mgr, RefPtr<Ogre::SceneNode> node )
			{
				if( !ptr ) return;
				if( scale != Vec3::ZERO )
					this->ptr->getParentSceneNode()->setScale( scale );
				this->ptr->setCastShadows( true );
			}
			
			SceneParameters::SceneParameters( ) : shadows( Ogre::SHADOWTYPE_STENCIL_ADDITIVE ), ambient( 1,1,1,1 ){}
			SceneParameters::~SceneParameters( ){ destroy(); }
			SceneParameters::SceneParameters( const Lit::Color& ambient_, Ogre::ShadowTechnique shadows_, size_t numLights ): ambient( ambient_ ), shadows( shadows_ ){ 
				if( numLights != 0 )
					lights.resize( numLights ); 
			}
					
			void SceneParameters::setMesh( const std::string &meshName, const Lit::Vec3& scale )
			{
				if( !created() ) return;
				entity.mesh = meshName;
				entity.scale = scale;
				try
				{
					entity.sync( ptr );
				}
				catch( std::exception & e )
				{
					Ogre::LogManager::getSingleton().logMessage("Cannot set project mesh '"+meshName+"' because "+std::string(e.what()) );
				}
			}

			SceneParameters::Light& SceneParameters::addLight( const SceneParameters::Light& light )
			{
				lights.push_back( light );
				if( created() ) lights.back().create( ptr );
				return lights.back();
			}
			
			SceneParameters::Light& SceneParameters::setLight( size_t id, const SceneParameters::Light& light ){
				if( id >= lights.size() ) lights.resize( id + 1 );
				lights[id] = light;
				return lights[id];
			}
			size_t SceneParameters::getLightCount() const{ return lights.size(); }
			void SceneParameters::clearLights( )
			{
				if( !created() ) return;
				for( auto i=lights.begin(); i!=lights.end(); i++ )
				{
					i->destroy( ptr );
				}
				lights.clear();
			}

			bool SceneParameters::created() const{ return node; }
			void SceneParameters::destroy( )
			{
				if( created() )
				{
					node->detachAllObjects( );
					for( auto i=lights.begin(); i!=lights.end(); i++ )
					{
						i->destroy( ptr );
					}
					entity.destroy(ptr);
					if( node )
						ptr->destroySceneNode( *node );
					ptr = NULL;
					node = NULL;
				}
			}
			void SceneParameters::create( RefPtr<Ogre::SceneManager> mgr )
			{
				ptr = mgr;
				if( created() )
				{
					destroy();
				}
				ptr = mgr;
				node = mgr->getRootSceneNode()->createChildSceneNode();
				entity.create(ptr, node);
				for( auto i=lights.begin(); i!=lights.end(); i++ )
				{
					i->create( ptr, node );
				}

				sync();
			}

			void SceneParameters::sync(RefPtr<Ogre::SceneManager> mgr)
			{
				if( !created() ){
					if( mgr ) create( mgr );
					return;
				}
				ptr->setAmbientLight( ambient );
				ptr->setShadowTechnique( shadows );
				entity.sync(ptr, node);
				for( auto i=lights.begin(); i!=lights.end(); i++ )
				{
					i->sync(ptr, node);
				}
			}
			void SceneParameters::fitMeshInViewport( RefPtr<Ogre::Camera> cam, Real offset )
			{
				AABB box(entity->getBoundingBox());
				box.scale( entity.scale );
				Ogre::Vector3 vSize = box.getHalfSize();
				Ogre::Vector3 vCenter = box.getCenter(); 
   
				vSize += Ogre::Vector3(vSize.z, vSize.z, vSize.z);

				float maxsize = std::max(std::max(vSize.x,vSize.y),vSize.z);
   
				vSize = Ogre::Vector3(0, 0, maxsize * offset) + vCenter;
   
				cam->setPosition(vSize.x,vSize.y,vSize.z);
				cam->lookAt(vCenter.x,vCenter.y,vCenter.z);
				//cam->setAutoTracking( true, entity->getParentSceneNode() );
			}
		};
	};
};